import numpy as np
from math import sqrt
from collections import Counter

# 参考波波老师的代码，用到了Python中的math和collections.Counter来完成统计操作
# 模仿skilearn库中的knn算法的封装方法，改造该方法
class KNNClassifier1_1:
    def __init__(self, k):
        """初始化kNN分类器"""
        assert k >= 1, "k值必须大于等于0"
        self.k = k
        self._X_train = None
        self._y_train = None

    def fit(self, X_train, y_train):
        """根据训练数据集的X_train和y_train训练kNN分类器"""
        assert self.k <= X_train.shape[0], "k值必须小于等于训练样本个数"
        assert X_train.shape[0] == y_train.shape[0], "训练样本个数必须和类型数相等"
        self._X_train = X_train
        self._y_train = y_train
        return self

    def predict(self, X_predict):
        assert self._X_train.shape[1] == X_predict.shape[1], "待预测的样本特征数必须和训练样本的特征数相等"
        y_predict = [self._predict(x) for x in X_predict]

        return np.array(y_predict)

    def _predict(self, x):
        # 计算样本a到每一个训练样本的距离
        distances = [sqrt(np.sum((x - x_train) ** 2)) for x_train in self._X_train]
        # 将样本a到各个训练样本的距离进行排序，获取排序后每个距离在原训练样本中的索引
        nearestDisIndexes = np.argsort(distances)

        # 获取距离样本a最近的k个样本的类型
        topK_y = [self._y_train[i] for i in nearestDisIndexes[:self.k]]

        # 统计不同类型的个数
        votes = Counter(topK_y)

        # 返回 距离样本a最近的 k个样本中 出现次数最多的类型 作为KNN算法预测的结果
        return votes.most_common(1)[0][0]

    def score(self, X_test, y_test):
        """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""
        print("score")